今天接續來回顧學習的內容,列出重點關鍵字,在回顧的時候有記憶點。
it('should call handleData when recieving new data', () => {
//Assign
const spy = spyOn(component,'handleData');
const getDataSubject = new BehaviorSubject<number>(3);
const apiService = TestBed.inject(ApiService);
spyOn(apiService,'getData').and.returnValue(getDataSubject.asObservable());
//Act
component.ngOnInit(); // Subscribe again since the function was just changed.
getDataSubject.next(5); // Emit a new value
//Assert
expect(spy).toHaveBeenCalledWith(5);
});
用來測試 「Route」,常用在測試登入成功、未登入
it('should navigate to the home page if the user is logged in', fakeAsync(() => {
//Assign
const router = TestBed.inject(Router);
const authorisationService = TestBed.inject(AuthorisationService);
spyOn(authorisationService, 'canActivate').and.returnValue(true); // The user is allowed to navigate
//Act
router.navigate(['home']); // try navigating home
tick(); // Skip ahead to when navigation is complete
//Assert
expect(router.url).toBe('/home');
}));
會需要用到 javascript prototype 取得 localstroage 的資料
it('should load data from local storage', () => {
//Assign
const data = "value";
const spy = spyOn(Storage.prototype, 'getItem').and.returnValue(JSON.stringify(data));
// const spy = spyOn(localStorage, 'setItem').and.returnValue(JSON.stringify(data)); //doesn't work in Firefox
//Act
component.loadData();
//Assert
expect(spy).toHaveBeenCalledWith("data");
})
changeDetecor,可以偵測 UI 上任何更新,但是要測試 change detector 無法 inject to TestBed,需要建立新元件,在透過該元件呼叫 constructor service
query(By.css('#title'))
querySelector('#title')
queryAll(By.css('.title'))
querySelectorAll('.title')
設定條件為true 或 false,在 query 其元素,根據情境預期成功或失敗
it('should show the title when showTitle is true', () => {
//Assign
component.showTitle = true;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title1'));
expect(element).toBeTruthy();
});
it('shouldnt show the title when showTitle is false', () => {
//Assign
component.showTitle = false;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title1'));
expect(element).toBeFalsy();
});
設定陣列元素,在 query 其元素,根據情境預期成功或失敗
describe('queryAll', () => {
it('should show the correct number of elements', () => {
//Assign
component.messages = ['a','b','c'];
//Act
componentFixture.detectChanges();
//Assert
const elements = componentFixture.debugElement.queryAll(By.css('.row'));
expect(elements.length).toEqual(3);
});
});
方法類似 ngIf,設定條件為true 或 false,在 query 其元素,根據情境預期成功或失敗
describe('querySelector', () => {
it('should apply the class active when activeTitle is true', () => {
//Assign
component.activeTitle = true;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.nativeElement.querySelector('#title1');
expect(element.classList).toContain('active');
});
})
重點在使用 key code trigger dropdown 元件
describe('query', () => {
it('should call the function selectOption when the user selects a new value from the dropdown', () => {
//Assign
component.options = ['option 1','option 2','option 3'];
component.option = 'option 1';
componentFixture.detectChanges();
const spy = spyOn(component, 'selectOption');
const element = componentFixture.debugElement.query(By.css('#optionDropdown'));
//Act
triggerKeyDownEvent(element, 32); // space to open the dropdown
triggerKeyDownEvent(element, 40); // down arrow
triggerKeyDownEvent(element, 13); // enter to select second option and close the dropdown
//Assert
expect(spy).toHaveBeenCalledWith('option 2');
});
})
function triggerKeyDownEvent(element: DebugElement, which: number, key = ''): void {
element.triggerEventHandler('keydown', {
which: which,
key: key,
preventDefault: () => { },
});
}
output event 會將其值 emit 回 parent component,所以要 new Event 物件帶入 output event name outputData
,在 query 其元素,根據情境預期成功或失敗
<app-child-component id="title" (outputData)="updateData($event)"></app-child-component>
describe('query', () => {
it('should call the correct function on child component output event', () => {
//Assign
const spy = spyOn(component,'updateData');
const event = new Event('outputData');
//Act
const element = componentFixture.debugElement.query(By.css('#title'));
element.nativeElement.dispatchEvent(event);
//Assert
expect(spy).toHaveBeenCalledWith(event);
});
})
取得 input value,在 query 其元素,根據情境預期成功或失敗
<app-child-component id="title" [inputData]="title"></app-child-component>
<button id="button" type="submit" [disabled]="isDisabled">Submit</button>
describe('query', () => {
it('should pass down the correct data to app-child-component', () => {
//Assign
component.title = "a title";
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title'));
expect(element.componentInstance.inputData).toEqual(component.title);
});
})
來到重點回顧的尾聲,紀錄實做面遇到各種測試項目會用到的「關鍵字」和需要留意的概念,在學習的過程中意識到「實做」和「研究」有著不同的個性,雖說描述起來有點抽象,但前者偏重於「經驗應用」,後者著重在「學者精神」,有著「10萬個為什麼的精神」確認每個用法,如何用?用在哪?
「實做應用」會需要一點「學者精神」才可以早點完成作業,但不論或早或晚,都是過程體驗中必經之路,也許這次的學習之旅認識到單元測試 (Unit Test) 支線,下個時間點會見識軟體開發其他面向。